class C:

    # 当C的对象被索引的时候会执行该魔法方法
    # 单个下标的索引操作以及切片操作都会执行到这里
    def __getitem__(self, index):
        print(f"魔法方法__getitem__被调用,传入的 index = {index}")


c = C()

# 因为我们实现了魔法方法__getitem__，所以在该对象进行下标索引计算的时候会去调用该魔法方法
r_2 = c[2]
# 魔魔法方法__getitem__被调用,传入的 index = 2

print("=" * 200)

# 切片操作
r_2_8 = c[2:8]

# 魔法方法__getitem__被调用,传入的 index = slice(2, 8, None)

# slice 函数就是切片操作的底层实现

print("================================================================================" * 3)


class D:

    def __init__(self, data):
        self.data = data

    # 不仅仅拦截索引跟切片的操作，获取操作也会被拦截
    def __getitem__(self, index):
        result = self.data[index] * 2
        print(F"魔法方法__getitem__被执行，传入的index={index}，返回的结果={result}")
        return result


print("================================================================================" * 3)

d = D([1, 2, 3, 4, 5])
for i in d:
    # 循环获取的时候也会被 __getitem__ 方法拦截
    # 但是，一般情况下，在迭代的时候建议使用魔法方法 __iter__(self) 和 __next__(self)
    print(i, end=' \n')

print("================================================================================" * 3)

# 如一个对象定义了 __iter__ 魔法方法，那么他就是一个可迭代对象
# 如果一个可迭代对象定义了 __next__ 魔法方法，那么他就是一个迭代器
# 列表是一个可迭代对象，不是一个迭代器，因为他没有 __next__ 方法

x = [1, 2, 3, 4, 5]
print(dir(x))
# 如下，列表x中有 __iter__ 方法，但是没有 __next__ 方法

# ['__add__', '__class__', '__class_getitem__', '__contains__', '__delattr__', '__delitem__',
# '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__',
# '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__',
# '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__',
# '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__',
# '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend',
# 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

print("================================================================================" * 3)

# 其实在循环的时候，它会先调用 __iter__ 得到一个迭代器，然后使用迭代器的 __next__ 方法进行获取元素的操作
_ = iter(x)
print(_)
# <list_iterator object at 0x109c82e20>
while True:
    try:
        i = _.__next__()
    except StopIteration:
        break
    print(i, end='\n')

print("================================================================================" * 3)


# 我们创建一个可以迭代的迭代器对象
class Double:

    def __init__(self, start, stop):
        self.value = start - 1
        self.stop = stop

    def __iter__(self):
        # 该方法的作用就是返回一个迭代器，该类自己就实现了 __next__ 方法，所以，它本身就是一个迭代器
        return self

    def __next__(self):
        if self.value == self.stop:
            raise StopIteration
        self.value += 1
        return self.value * 2


d = Double(1, 5)
for i in d:
    print(i, end=' ')
# 2 4 6 8 10
